package dao

import (
	"context"
	"time"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"

	"mychat/internal/common/logger"
	"mychat/internal/data/dao/model"
)

type FriendDao interface {
	GetUserFriend(ctx context.Context, userId, friendId uint) (*model.UserFreind, error)
	AddUserFriend(ctx context.Context, userId, friendId uint) error
	DeleteUserFriend(ctx context.Context, userId, friendId uint) error
}

type friendDao struct {
	db *gorm.DB
	lg logger.Logger
}

func NewFriendDao(db *gorm.DB, lg logger.Logger) FriendDao {
	return &friendDao{
		db: db,
		lg: lg.WithCustomTag(logger.LoggerTagMySQL),
	}
}

func (d *friendDao) GetUserFriend(ctx context.Context, userId, friendId uint) (*model.UserFreind, error) {
	db := d.db.WithContext(ctx)
	var ufriend model.UserFreind
	err := db.Table(ufriend.TableName()).
		Where("user_id = ? and friend_id = ? and deleted_at = 0", userId, friendId).
		First(&ufriend).
		Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return &ufriend, nil
}

func (d *friendDao) AddUserFriend(ctx context.Context, userId, friendId uint) error {
	dbtx := d.db.Begin()
	nowTime := time.Now()
	ufriend := &model.UserFreind{
		UserId:         userId,
		FriendId:       friendId,
		RelatedAt:      nowTime,
		LastAccessedAt: nowTime,
		DeletedAt:      0,
	}

	err := d.upsertUserFriend(dbtx, ufriend)
	if err != nil {
		return err
	}

	ufriend.UserId = friendId
	ufriend.FriendId = userId
	err = d.upsertUserFriend(dbtx, ufriend)
	if err != nil {
		return err
	}

	err = dbtx.Commit().Error
	if err != nil {
		return err
	}
	return nil
}

func (d *friendDao) upsertUserFriend(db *gorm.DB, ufriend *model.UserFreind) error {
	err := db.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "user_id"}, {Name: "friend_id"}},
		UpdateAll: true,
	}).Create(ufriend).Error

	if err != nil {
		return err
	}
	return nil
}

func (d *friendDao) DeleteUserFriend(ctx context.Context, userId, friendId uint) error {
	dbtx := d.db.Begin()
	nowTime := time.Now()
	ufriend := &model.UserFreind{
		UserId:         userId,
		FriendId:       friendId,
		RelatedAt:      nowTime,
		LastAccessedAt: nowTime,
		DeletedAt:      time.Now().Unix(),
	}

	err := d.upsertUserFriend(dbtx, ufriend)
	if err != nil {
		return err
	}

	ufriend.UserId = friendId
	ufriend.FriendId = userId
	err = d.upsertUserFriend(dbtx, ufriend)
	if err != nil {
		return err
	}

	err = dbtx.Commit().Error
	if err != nil {
		return err
	}
	return nil
}
